﻿using ICodeShare.UI.Controls.Primitives;
using System;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Controls.Primitives;
using System.Windows.Data;
using System.Windows.Media;
using System.Windows.Media.Animation;

namespace ICodeShare.UI.Controls
{
    /// <summary>
    /// A Button that allows the user to toggle between two states.
    /// </summary>
    [TemplatePart(Name = PART_BackgroundTranslate, Type = typeof(TranslateTransform))]
    [TemplatePart(Name = PART_DraggingThumb, Type = typeof(Thumb))]
    [TemplatePart(Name = PART_SwitchTrack, Type = typeof(Grid))]
    [TemplatePart(Name = PART_Thumb, Type = typeof(Border))]
    [TemplatePart(Name = PART_ThumbTranslate, Type = typeof(TranslateTransform))]
    public class SwitchButton : ToggleButtonBase
    {
        #region Constants

        private const string PART_BackgroundTranslate = "PART_BackgroundTranslate";
        private const string PART_DraggingThumb = "PART_DraggingThumb";
        private const string PART_SwitchTrack = "PART_SwitchTrack";
        private const string PART_Thumb = "PART_Thumb";
        private const string PART_ThumbTranslate = "PART_ThumbTranslate";

        #endregion Constants

        #region Members

        private TranslateTransform _BackgroundTranslate;
        private Thumb _DraggingThumb;
        private Grid _SwitchTrack;
        private Border _Thumb;
        private TranslateTransform _ThumbTranslate;

        private double? _lastDragPosition;
        private bool _isDragging;

        private DoubleAnimation _thumbAnimation;

        #endregion Members

        #region Constructors

        static SwitchButton()
        {
            //设置用于引用此控件的样式，方式一
            DefaultStyleKeyProperty.OverrideMetadata(typeof(SwitchButton), new FrameworkPropertyMetadata(typeof(SwitchButton)));
        }

        public SwitchButton()
        {
            //设置用于引用此控件的样式，方式二
            DefaultStyleKey = typeof(SwitchButton);
            Checked += ToggleSwitchButton_Checked;
            Unchecked += ToggleSwitchButton_Unchecked;
        }

        ~SwitchButton()
        {
        }

        #endregion Constructors

        #region Properties


        #region CornerRadius 外侧圆角

        /// <summary>
        /// 外侧圆角
        /// </summary>
        public CornerRadius CornerRadius
        {
            get { return (CornerRadius)GetValue(CornerRadiusProperty); }
            set { SetValue(CornerRadiusProperty, value); }
        }

        public static readonly DependencyProperty CornerRadiusProperty =
            DependencyProperty.Register("CornerRadius", typeof(CornerRadius), typeof(SwitchButton), new PropertyMetadata(new CornerRadius()));

        #endregion CornerRadius 外侧圆角

        #region On 开启属性

        #region OnLabel On文字

        /// <summary>
        /// Gets/sets the text to display when the control is in it's On state.
        /// </summary>
        public string OnLabel
        {
            get { return (string)GetValue(OnLabelProperty); }
            set { SetValue(OnLabelProperty, value); }
        }

        public static readonly DependencyProperty OnLabelProperty =
            DependencyProperty.Register("OnLabel", typeof(string), typeof(SwitchButton), new PropertyMetadata("On"));

        #endregion OnLabel On文字

        #region OnLabelForeground On文字颜色

        /// <summary>
        /// Gets/sets the foreground to display when the control is in it's On state.
        /// </summary>
        public Brush OnLabelForeground
        {
            get { return (Brush)GetValue(OnLabelForegroundProperty); }
            set { SetValue(OnLabelForegroundProperty, value); }
        }

        public static readonly DependencyProperty OnLabelForegroundProperty =
            DependencyProperty.Register("OnLabelForeground", typeof(Brush), typeof(SwitchButton), new PropertyMetadata(Brushes.DarkSlateBlue));

        #endregion OnLabelForeground On文字颜色

        #region OnBackground On背景颜色

        /// <summary>
        /// Gets/sets the brush used for the on-switch's foreground.
        /// </summary>
        public Brush OnBackground
        {
            get { return (Brush)GetValue(OnBackgroundProperty); }
            set { SetValue(OnBackgroundProperty, value); }
        }

        public static readonly DependencyProperty OnBackgroundProperty =
            DependencyProperty.Register("OnBackground", typeof(Brush), typeof(SwitchButton), null);

        #endregion OnBackground On背景颜色

        #endregion On 开启属性

        #region Off 关闭属性

        #region OffLabel Off文字

        /// <summary>
        /// Gets/sets the text to display when the control is in it's Off state.
        /// </summary>
        public string OffLabel
        {
            get { return (string)GetValue(OffLabelProperty); }
            set { SetValue(OffLabelProperty, value); }
        }

        public static readonly DependencyProperty OffLabelProperty =
            DependencyProperty.Register("OffLabel", typeof(string), typeof(SwitchButton), new PropertyMetadata("Off"));

        #endregion OffLabel Off文字

        #region OffLabelForeground Off文字颜色

        /// <summary>
        /// Gets/sets the foreground to display when the control is in it's Off state.
        /// </summary>
        public Brush OffLabelForeground
        {
            get { return (Brush)GetValue(OffLabelForegroundProperty); }
            set { SetValue(OffLabelForegroundProperty, value); }
        }

        public static readonly DependencyProperty OffLabelForegroundProperty =
            DependencyProperty.Register("OffLabelForeground", typeof(Brush), typeof(SwitchButton), new PropertyMetadata(Brushes.DarkSlateBlue));

        #endregion OffLabelForeground Off文字颜色

        #region OffBackground Off背景颜色

        /// <summary>
        /// Gets/sets the brush used for the off-switch's foreground.
        /// </summary>
        public Brush OffBackground
        {
            get { return (Brush)GetValue(OffBackgroundProperty); }
            set { SetValue(OffBackgroundProperty, value); }
        }

        public static readonly DependencyProperty OffBackgroundProperty =
            DependencyProperty.Register("OffBackground", typeof(Brush), typeof(SwitchButton), null);

        #endregion OffBackground Off背景颜色

        #endregion Off 关闭属性

        #region Thumb滑块属性

        #region ThumbBorderBrush 滑块边框颜色

        /// <summary>
        /// Gets/sets the brush used for the thumb indicator.
        /// </summary>
        public Brush ThumbBorderBrush
        {
            get { return (Brush)GetValue(ThumbBorderBrushProperty); }
            set { SetValue(ThumbBorderBrushProperty, value); }
        }

        public static readonly DependencyProperty ThumbBorderBrushProperty =
            DependencyProperty.Register("ThumbBorderBrush", typeof(Brush), typeof(SwitchButton), null);

        #endregion ThumbBorderBrush 滑块边框颜色

        #region ThumbBackground 滑块背景颜色

        /// <summary>
        /// Gets/sets the Background used for the thumb indicator.
        /// </summary>
        public Brush ThumbBackground
        {
            get { return (Brush)GetValue(ThumbBackgroundProperty); }
            set { SetValue(ThumbBackgroundProperty, value); }
        }

        public static readonly DependencyProperty ThumbBackgroundProperty =
            DependencyProperty.Register("ThumbBackground", typeof(Brush), typeof(SwitchButton), new PropertyMetadata(Brushes.WhiteSmoke));

        #endregion ThumbBackground 滑块背景颜色

        #region ThumbBorderThickness 滑块边框线大小

        /// <summary>
        /// Gets/sets the border Thickness used for the thumb indicator.
        /// </summary>
        public Thickness ThumbBorderThickness
        {
            get { return (Thickness)GetValue(ThumbBorderThicknessProperty); }
            set { SetValue(ThumbBorderThicknessProperty, value); }
        }

        public static readonly DependencyProperty ThumbBorderThicknessProperty =
            DependencyProperty.Register("ThumbBorderThickness", typeof(Thickness), typeof(SwitchButton), new PropertyMetadata(new Thickness(0)));

        #endregion ThumbBorderThickness 滑块边框线大小

        #region ThumbDisabledBrush 滑块不可用颜色

        /// <summary>
        /// Gets/sets the brush used for the thumb indicator.
        /// </summary>
        public Brush ThumbDisabledBrush
        {
            get { return (Brush)GetValue(ThumbDisabledBrushProperty); }
            set { SetValue(ThumbDisabledBrushProperty, value); }
        }

        public static readonly DependencyProperty ThumbDisabledBrushProperty =
            DependencyProperty.Register("ThumbDisabledBrush", typeof(Brush), typeof(SwitchButton), null);

        #endregion ThumbDisabledBrush 滑块不可用颜色

        #region ThumbWidth 滑块宽度

        /// <summary>
        /// Gets/sets the width of the thumb indicator.
        /// </summary>
        public double ThumbWidth
        {
            get { return (double)GetValue(ThumbWidthProperty); }
            set { SetValue(ThumbWidthProperty, value); }
        }

        public static readonly DependencyProperty ThumbWidthProperty =
            DependencyProperty.Register("ThumbWidth", typeof(double), typeof(SwitchButton), new PropertyMetadata(13d));

        #endregion ThumbWidth 滑块宽度

        #region ThumbCornerRadius 滑块外侧圆角

        /// <summary>
        /// 滑块圆角
        /// </summary>
        public CornerRadius ThumbCornerRadius
        {
            get { return (CornerRadius)GetValue(ThumbCornerRadiusProperty); }
            set { SetValue(ThumbCornerRadiusProperty, value); }
        }

        public static readonly DependencyProperty ThumbCornerRadiusProperty =
            DependencyProperty.Register("ThumbCornerRadius", typeof(CornerRadius), typeof(SwitchButton), new PropertyMetadata(new CornerRadius()));

        #endregion ThumbCornerRadius 滑块外侧圆角

        #endregion Thumb滑块属性

        #region ICommand 路由命令

        //#region CheckChangedCommand 选中状态变更命令

        ///// <summary>
        ///// Gets/sets the command which will be executed if the IsChecked property was changed.
        ///// </summary>
        //public ICommand CheckChangedCommand
        //{
        //    get { return (ICommand)GetValue(CheckChangedCommandProperty); }
        //    set { SetValue(CheckChangedCommandProperty, value); }
        //}

        //public static readonly DependencyProperty CheckChangedCommandProperty =
        //    DependencyProperty.Register("CheckChangedCommand", typeof(ICommand), typeof(SwitchButton), new PropertyMetadata(null));

        //#endregion

        //#region CheckChangedCommandParameter

        ///// <summary>
        ///// Gets/sets the command parameter which will be passed by the CheckChangedCommand.
        ///// </summary>
        //public object CheckChangedCommandParameter
        //{
        //    get { return (object)GetValue(CheckChangedCommandParameterProperty); }
        //    set { SetValue(CheckChangedCommandParameterProperty, value); }
        //}

        //public static readonly DependencyProperty CheckChangedCommandParameterProperty =
        //    DependencyProperty.Register("CheckChangedCommandParameter", typeof(object), typeof(SwitchButton), new PropertyMetadata(null));

        //#endregion

        //#region CheckedCommand

        ///// <summary>
        ///// Gets/sets the command which will be executed if the checked event of the control is fired.
        ///// </summary>
        //public ICommand CheckedCommand
        //{
        //    get { return (ICommand)GetValue(CheckedCommandProperty); }
        //    set { SetValue(CheckedCommandProperty, value); }
        //}

        //public static readonly DependencyProperty CheckedCommandProperty =
        //    DependencyProperty.Register("CheckedCommand", typeof(ICommand), typeof(SwitchButton), new PropertyMetadata(null));

        //#endregion

        //#region CheckedCommandParameter

        ///// <summary>
        ///// Gets/sets the command parameter which will be passed by the UnCheckedCommand.
        ///// </summary>
        //public object UnCheckedCommandParameter
        //{
        //    get { return (object)GetValue(UnCheckedCommandParameterProperty); }
        //    set { SetValue(UnCheckedCommandParameterProperty, value); }
        //}

        ///// <summary>
        ///// Gets/sets the command parameter which will be passed by the CheckedCommand.
        ///// </summary>
        //public object CheckedCommandParameter
        //{
        //    get { return (object)GetValue(CheckedCommandParameterProperty); }
        //    set { SetValue(CheckedCommandParameterProperty, value); }
        //}

        //public static readonly DependencyProperty CheckedCommandParameterProperty =
        //    DependencyProperty.Register("CheckedCommandParameter", typeof(object), typeof(SwitchButton), new PropertyMetadata(null));

        //#endregion

        //#region UnCheckedCommand

        ///// <summary>
        ///// Gets/sets the command which will be executed if the checked event of the control is fired.
        ///// </summary>
        //public ICommand UnCheckedCommand
        //{
        //    get { return (ICommand)GetValue(UnCheckedCommandProperty); }
        //    set { SetValue(UnCheckedCommandProperty, value); }
        //}

        //public static readonly DependencyProperty UnCheckedCommandProperty =
        //    DependencyProperty.Register("UnCheckedCommand", typeof(ICommand), typeof(SwitchButton), new PropertyMetadata(null));

        //#endregion

        //#region UnCheckedCommandParameter

        //public static readonly DependencyProperty UnCheckedCommandParameterProperty =
        //    DependencyProperty.Register("UnCheckedCommandParameter", typeof(object), typeof(SwitchButton), new PropertyMetadata(null));

        //#endregion

        #endregion ICommand 路由命令

        #region SwitchForeground 失效属性示例

        /// <summary>
        /// Gets/sets the brush used for the switch's foreground.
        /// </summary>
        [Obsolete(@"This property will be deleted in the next release. You should use OnSwitchBrush and OffSwitchBrush to change the switch's brushes.")]
        public Brush SwitchForeground
        {
            get { return (Brush)GetValue(SwitchForegroundProperty); }
            set { SetValue(SwitchForegroundProperty, value); }
        }

        [Obsolete(@"This property will be deleted in the next release. You should use OnSwitchBrush and OffSwitchBrush to change the switch's brushes.")]
        public static readonly DependencyProperty SwitchForegroundProperty = DependencyProperty.Register("SwitchForeground", typeof(Brush), typeof(SwitchButton), new PropertyMetadata(null, (o, e) => ((SwitchButton)o).OnBackground = e.NewValue as Brush));

        #endregion SwitchForeground 失效属性示例

        #endregion Properties

        #region Methods

        #region 重写方法

        protected override void OnToggle()
        {
            IsChecked = IsChecked != true;
            UpdateThumb();
        }

        public override void OnApplyTemplate()
        {
            base.OnApplyTemplate();
            _BackgroundTranslate = GetTemplateChild(PART_BackgroundTranslate) as TranslateTransform;
            _DraggingThumb = GetTemplateChild(PART_DraggingThumb) as Thumb;
            _SwitchTrack = GetTemplateChild(PART_SwitchTrack) as Grid;
            _Thumb = GetTemplateChild(PART_Thumb) as Border;
            _ThumbTranslate = GetTemplateChild(PART_ThumbTranslate) as TranslateTransform;

            if (_Thumb != null && _BackgroundTranslate != null)
            {
                Binding translationBinding;
                translationBinding = new System.Windows.Data.Binding("X");
                translationBinding.Source = _ThumbTranslate;
                BindingOperations.SetBinding(_BackgroundTranslate, TranslateTransform.XProperty, translationBinding);
            }

            if (_DraggingThumb != null && _SwitchTrack != null && _Thumb != null && _ThumbTranslate != null)
            {
                _DraggingThumb.DragStarted += _DraggingThumb_DragStarted;
                _DraggingThumb.DragDelta += _DraggingThumb_DragDelta;
                _DraggingThumb.DragCompleted += _DraggingThumb_DragCompleted;

                _SwitchTrack.SizeChanged += _SwitchTrack_SizeChanged;
            }
        }

        #endregion 重写方法

        #region Routed Event路由事件

        /// <summary>
        /// 选中事件
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void ToggleSwitchButton_Checked(object sender, RoutedEventArgs e)
        {
            UpdateThumb();
        }

        /// <summary>
        /// 未选中事件
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void ToggleSwitchButton_Unchecked(object sender, RoutedEventArgs e)
        {
            UpdateThumb();
        }

        private void _DraggingThumb_DragStarted(object sender, DragStartedEventArgs e)
        {
            if (_ThumbTranslate != null)
            {
                _ThumbTranslate.BeginAnimation(TranslateTransform.XProperty, null);
                double destination = IsChecked.GetValueOrDefault() ? ActualWidth - (_SwitchTrack.Margin.Left + _SwitchTrack.Margin.Right + _Thumb.ActualWidth + this.BorderThickness.Left + this.BorderThickness.Right + this.Padding.Left + this.Padding.Right) : 0;
                _ThumbTranslate.X = destination;
                _thumbAnimation = null;
            }
            _lastDragPosition = _ThumbTranslate.X;
            _isDragging = false;
        }

        private void _DraggingThumb_DragDelta(object sender, DragDeltaEventArgs e)
        {
            if (_lastDragPosition.HasValue)
            {
                if (Math.Abs(e.HorizontalChange) > 3)
                    _isDragging = true;
                if (_SwitchTrack != null && _Thumb != null)
                {
                    double lastDragPosition = _lastDragPosition.Value;
                    _ThumbTranslate.X = Math.Min(ActualWidth - (_SwitchTrack.Margin.Left + _SwitchTrack.Margin.Right + _Thumb.ActualWidth + this.BorderThickness.Left + this.BorderThickness.Right + this.Padding.Left + this.Padding.Right), Math.Max(0, lastDragPosition + e.HorizontalChange));
                }
            }
        }

        private void _DraggingThumb_DragCompleted(object sender, DragCompletedEventArgs e)
        {
            _lastDragPosition = null;
            if (!_isDragging)
            {
                OnToggle();
            }
            else if (_ThumbTranslate != null && _SwitchTrack != null)
            {
                if (!IsChecked.GetValueOrDefault() && _ThumbTranslate.X + 6.5 >= _SwitchTrack.ActualWidth / 2)
                {
                    OnToggle();
                }
                else if (IsChecked.GetValueOrDefault() && _ThumbTranslate.X + 6.5 <= _SwitchTrack.ActualWidth / 2)
                {
                    OnToggle();
                }
                else UpdateThumb();
            }
        }

        private void _SwitchTrack_SizeChanged(object sender, SizeChangedEventArgs e)
        {
            if (_ThumbTranslate != null && _SwitchTrack != null && _Thumb != null)
            {
                double destination = IsChecked.GetValueOrDefault() ? ActualWidth - (_SwitchTrack.Margin.Left + _SwitchTrack.Margin.Right + _Thumb.ActualWidth + this.BorderThickness.Left + this.BorderThickness.Right + this.Padding.Left + this.Padding.Right) : 0;
                _ThumbTranslate.X = destination;
            }
        }

        #endregion Routed Event路由事件

        private void UpdateThumb()
        {
            if (_ThumbTranslate != null && _SwitchTrack != null && _Thumb != null)
            {
                double destination = IsChecked.GetValueOrDefault() ? ActualWidth - (_SwitchTrack.Margin.Left + _SwitchTrack.Margin.Right + _Thumb.ActualWidth + this.BorderThickness.Left + this.BorderThickness.Right + this.Padding.Left + this.Padding.Right) : 0;

                _thumbAnimation = new DoubleAnimation();
                _thumbAnimation.To = destination;
                _thumbAnimation.Duration = TimeSpan.FromMilliseconds(500);
                _thumbAnimation.EasingFunction = new ExponentialEase() { Exponent = 9 };
                _thumbAnimation.FillBehavior = FillBehavior.Stop;

                AnimationTimeline currentAnimation = _thumbAnimation;
                _thumbAnimation.Completed += (sender, e) =>
                {
                    if (_thumbAnimation != null && currentAnimation == _thumbAnimation)
                    {
                        _ThumbTranslate.X = destination;
                        _thumbAnimation = null;
                    }
                };
                _ThumbTranslate.BeginAnimation(TranslateTransform.XProperty, _thumbAnimation);
            }
        }

        #endregion Methods
    }
}